package com.matuw.android.util;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;

import com.matuw.androidSdk;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;


public class UriUtils {

    /**
     * Uri to file.
     *
     * @param uri The uri.
     * @return file
     */
    public static File uri2File(final Uri uri) {
        if (uri == null) return null;
        File file = uri2FileReal(uri);
        if (file != null) return file;
        return copyUri2Cache(uri);
    }

    /**
     * Uri to file.
     *
     * @param uri The uri.
     * @return file
     */
    private static File uri2FileReal(final Uri uri) {
        String scheme = uri.getScheme();
        String path = uri.getPath();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && path != null) {
            File file = uri2FileRealApi24(path);
            if (file != null) return file;
        }

        if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            if (path != null) return new File(path);
            return null;
        }

        File file = uri2FileRealByDocumentUri(uri);
        if (file != null) return file;

        if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
            return getFileFromUri(uri);
        }

        return null;
    }

    private static File uri2FileRealApi24(String path) {
        String[] externals = new String[]{"/external/", "/external_path/"};
        String externalDir = Environment.getExternalStorageDirectory().getAbsolutePath();

        for (String external : externals) {
            if (path.startsWith(external)) {
                File file = new File(externalDir + path.replace(external, "/"));
                if (file.exists()) return file;
            }
        }

        Application app = QinSdk.getApp();
        String[] paths = {"/files_path/", "/cache_path/", "/external_files_path/", "/external_cache_path/"};
        for (int i = 0; i < paths.length; i++) {
            String prefix = paths[i];
            if (!path.startsWith(prefix)) continue;

            File parent;
            switch (i) {
                case 0:
                    parent = app.getFilesDir();
                    break;
                case 1:
                    parent = app.getCacheDir();
                    break;
                case 2:
                    parent = app.getExternalFilesDir(null);
                    break;
                case 3:
                    parent = app.getExternalCacheDir();
                    break;
                default:
                    return null;
            }

            if (parent == null) return null;
            String newPath = path.replace(prefix, File.separator);
            File file = new File(parent.getAbsolutePath() + newPath);
            if (file.exists()) return file;
        }

        return null;
    }

    @SuppressLint("DiscouragedPrivateApi")
    private static File uri2FileRealByDocumentUri(Uri uri) {
        Application application = QinSdk.getApp();
        if (!DocumentsContract.isDocumentUri(application, uri)) {
            return null;
        }

        String authority = uri.getAuthority();

        if ("com.android.externalstorage.documents".equals(authority)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            if (split.length < 2) return null;

            final String type = split[0];
            if ("primary".equalsIgnoreCase(type)) {
                return new File(Environment.getExternalStorageDirectory() + "/" + split[1]);
            }

            // Below logic is how External Storage provider build URI for documents
            // http://stackoverflow.com/questions/28605278/android-5-sd-card-label
            StorageManager mStorageManager = (StorageManager) application.getSystemService(Context.STORAGE_SERVICE);
            try {
                Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
                Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
                Method getUuid = storageVolumeClazz.getMethod("getUuid");
                Method getState = storageVolumeClazz.getMethod("getState");


                Method getPath = Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q ? storageVolumeClazz.getMethod("getPath") : null;
                Method getDirectory = Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q ? null : storageVolumeClazz.getMethod("getDirectory");

                Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
                Method isEmulated = storageVolumeClazz.getMethod("isEmulated");

                Object result = getVolumeList.invoke(mStorageManager);

                final int length = result == null ? 0 : Array.getLength(result);
                for (int i = 0; i < length; i++) {
                    Object storageVolumeElement = Array.get(result, i);

                    final boolean mounted = Environment.MEDIA_MOUNTED.equals(getState.invoke(storageVolumeElement))
                            || Environment.MEDIA_MOUNTED_READ_ONLY.equals(getState.invoke(storageVolumeElement));

                    //if the media is not mounted, we need not get the volume details
                    if (!mounted) continue;

                    //Primary storage is already handled.
                    if (Boolean.TRUE.equals(isPrimary.invoke(storageVolumeElement))
                            && Boolean.TRUE.equals(isEmulated.invoke(storageVolumeElement))) {
                        continue;
                    }

                    String uuid = (String) getUuid.invoke(storageVolumeElement);

                    if (uuid != null && uuid.equals(type)) {
                        String path = split[1];
                        Method method = getPath == null ? getDirectory : getPath;
                        Object mPath = method.invoke(storageVolumeElement);
                        return mPath == null ? null : new File(mPath.toString(), path);
                    }
                }
            } catch (Exception ignored) {
            }

            return null;
        }

        if ("com.android.providers.downloads.documents".equals(authority)) {
            String id = DocumentsContract.getDocumentId(uri);
            if (TextUtils.isEmpty(id)) {
                return null;
            }
            if (id.startsWith("raw:")) {
                return new File(id.substring(4));
            }

            if (id.startsWith("msf:")) {
                id = id.split(":")[1];
            }

            long availableId;
            try {
                availableId = Long.parseLong(id);
            } catch (Exception e) {
                return null;
            }

            String[] contentUriPrefixesToTry = new String[]{
                    "content://downloads/public_downloads",
                    "content://downloads/all_downloads",
                    "content://downloads/my_downloads",
            };

            for (String contentUriPrefix : contentUriPrefixesToTry) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), availableId);
                File file = getFileFromUri(contentUri);
                if (file != null) return file;
            }

            return null;
        }

        if ("com.android.providers.media.documents".equals(authority)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            if (split.length < 2) return null;

            List<String> contentUriTypes = Arrays.asList("image", "video", "audio");
            final String type = split[0];
            if (!contentUriTypes.contains(type)) {
                return null;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[]{split[1]};
            return getFileFromUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs);
        }

        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            return getFileFromUri(uri);
        }

        return null;
    }

    private static File getFileFromUri(Uri uri) {
        return getFileFromUri(uri, null, null);
    }

    private static File getFileFromUri(Uri uri, String selection, String[] selectionArgs) {
        String authority = uri.getAuthority();

        if ("com.google.android.apps.photos.content".equals(authority)) {
            if (!TextUtils.isEmpty(uri.getLastPathSegment())) {
                return new File(uri.getLastPathSegment());
            }
        }

        if ("com.tencent.mtt.fileprovider".equals(authority)) {
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                File fileDir = Environment.getExternalStorageDirectory();
                return new File(fileDir, path.substring("/QQBrowser".length()));
            }
        }

        if ("com.huawei.hidisk.fileprovider".equals(authority)) {
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                return new File(path.replace("/root", ""));
            }
        }

        ContentResolver resolver = QinSdk.getApp().getContentResolver();
        try (Cursor cursor = resolver.query(uri, new String[]{MediaStore.MediaColumns.DATA}, selection, selectionArgs, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                final int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
                if (columnIndex > -1) {
                    return new File(cursor.getString(columnIndex));
                }
            }
        } catch (Exception ignored) {
        }

        return null;
    }

    private static File copyUri2Cache(Uri uri) {
        Application application = QinSdk.getApp();
        try (InputStream is = application.getContentResolver().openInputStream(uri)) {
            File file = File.createTempFile(QinSdk.LIBRARY_PREFIX, null, application.getCacheDir());
            boolean rs = FileIOUtils.writeFile(file, is);
            return rs ? file : null;
        } catch (Exception e) {
            return null;
        }
    }
}
